# Copyright (c) 2006 "Gardiner"
# 
# Permission is hereby granted, free of charge, to any person obtaining a copy of
# this software and associated documentation files (the "Software"), to deal in
# the Software without restriction, including without limitation the rights to
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
# of the Software, and to permit persons to whom the Software is furnished to do
# so, subject to the following conditions:
# 
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
# 
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

require 'util'

class TendingSequence
	attr :vineyards
	attr :vineyardIndex
	attr :strainTendings
	attr :tendingIndex
	attr :sequence
	
	attr :maxFitness
	attr :maxFitnessIndex
	attr :bestStats
	attr :lastTending

	def initialize(vineyards, vineyardIndex, strainTendings, tendingIndex, seq = nil)
		@vineyards = vineyards
		@vineyardIndex = vineyardIndex
		@strainTendings = strainTendings
		@tendingIndex = tendingIndex
		
		if !seq
			@sequence = Array.new(100) { |t| t = rand(WineStrings.tendings.length) }
		else
			@sequence = seq
		end
		
		self.reset_values()
	end
	
	def reset_values	
		@maxFitness = 0
		@maxFitnessIndex = 99		
		@bestStats = @strainTendings[@tendingIndex].tendings[WineStrings.states.index('Initial')][0].clone
		@lastTending = @vineyards[@vineyardIndex].states.length
	end
	
	def evaluate(evaluateStats)
		total = @strainTendings[@tendingIndex].tendings[WineStrings.states.index('Initial')][0].clone
		grapeIndex = WineStrings.stats.index('Grapes')
		vigorIndex = WineStrings.stats.index('Vigor')
				
		@vineyards[@vineyardIndex].states.length.times { |i|
			
			# Looks like a barcode...
			totalTemp = Array.parallel_collect(total, 
					@strainTendings[@tendingIndex].tendings[@vineyards[@vineyardIndex].states[i]][@sequence[i]]) { |a, b| [a.to_f+b, 0.0].max }
		
			if totalTemp[grapeIndex] <= 0 || totalTemp[vigorIndex] <= 0
				@lastTending = i
				break
			end
			
			curFitness = evaluateStats.call(totalTemp, i+1)
			
			if curFitness > @maxFitness
				@maxFitness = curFitness
				@maxFitnessIndex = i
				@bestStats = totalTemp.clone
			end
			
			total = totalTemp
		}

		@maxFitness
	end
	
	def mutate()
		toMutate = rand
		
		if toMutate < 0.15
			@vineyardIndex = rand(@vineyards.length)
		elsif toMutate < 0.3
			@tendingIndex = rand(@strainTendings.length)
		else
			sequence[rand(@sequence.length)] = rand(WineStrings.tendings.length)
		end
	end
	
	def two_point_recombine(sequence2)
		point1 = rand([lastTending, sequence2.lastTending].min)
		point2 = rand([lastTending, sequence2.lastTending].min)
		
		point1, point2 = [point1, point2].min, [point1, point2].max
		
		@sequence = @sequence.slice(0, point1) + sequence2.sequence.slice(point1, point2 - point1) + @sequence.slice(point2, @sequence.length - point2)
		self.reset_values()
		
		if rand < 0.5
			@vineyardIndex = sequence2.vineyardIndex
			@tendingIndex = sequence2.tendingIndex
		end
	end
	
	def to_s
		str = "#{vineyards[vineyardIndex].vineyardName}, #{strainTendings[@tendingIndex].strainName}:\n"
		
		sequence.each_with_index { |val, idx|
			if idx >= @lastTending || idx > @maxFitnessIndex
				break
			end
			
			str << WineStrings.shortTendings[val] + "\n"
		}
				
		str << sprintf("\n %8s %8s %8s %8s %8s %8s %8s\n %8d %8d %8d %8d %8d %8d %8d\n", *(WineStrings.stats + @bestStats))
		
		str
	end
end